package com.example.burstcapture;

import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.util.Log;
import android.view.Surface;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

public class BurstCaptureFocus {
    CaptureRequest.Builder mStillBuilder = null;
    CameraCaptureSession mCaptureSession = null;
    CameraDevice mCameraDevice = null;
    Surface mImageSurface;
    ImageReader mImageReader;
    int mImageTaken = 0;
    List<Float> mFocus_distances = new ArrayList<>();
    List<CaptureRequest> mRequests = new ArrayList<>();
    public BurstCaptureFocus(CameraDevice cameraDevice, CameraCaptureSession captureSession, ImageReader imageReader, CameraCharacteristics characteristics){
        Log.e("TAG", "66666666666 ");
        mCameraDevice = cameraDevice;
        mCaptureSession = captureSession;
        mImageReader = imageReader;
        mImageSurface = mImageReader.getSurface();
        mImageReader.setOnImageAvailableListener(mReaderImgListener, null);

        try {
            mStillBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            mStillBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
            mStillBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_OFF);
            mStillBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.FLASH_MODE_TORCH); // just in case
            mStillBuilder.addTarget(mImageSurface);
            CaptureRequest request = mStillBuilder.build();


            mStillBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_OFF); // just in case


            mFocus_distances = setupFocusBracketingDistances(0.0f, 0.0f, 3);
            for(int i=0;i<mFocus_distances.size();i++) {
                mStillBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, mFocus_distances.get(i));
                mRequests.add(mStillBuilder.build());
            }

//            mCaptureSession.stopRepeating();
//            for (int i = 0; i<3; i++){
//                mCaptureSession.capture(requests.get(i), null, null);
//            }
            // 为了保证每一帧都perform control 设置的focus值 ，不能使用captureBurst,而且需要在等imageAvilable的时候，发送下一个request
            mCaptureSession.capture(mRequests.get(0), null, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    public static List<Float> setupFocusBracketingDistances(float source, float target, int count) {
        List<Float> focus_distances = new ArrayList<>();
        float focus_distance_s = source;
        float focus_distance_e = target;
//        final float max_focus_bracket_distance_c = 0.1f; // 10m 效果不好
        final float max_focus_bracket_distance_c = 0.5f;
        focus_distance_s = Math.max(focus_distance_s, max_focus_bracket_distance_c); // since we'll dealing with 1/distance, use Math.max
        focus_distance_e = Math.max(focus_distance_e, max_focus_bracket_distance_c); // since we'll dealing with 1/distance, use Math.max
        // we want to interpolate linearly in distance, not 1/distance
        float real_focus_distance_s = 1.0f/focus_distance_s;
        float real_focus_distance_e = 1.0f/focus_distance_e;
        for(int i=0;i<count;i++) {
            // for first and last, we still use the real focus distances; for intermediate values, we interpolate
            // with first/last clamped to max of 10m (to avoid taking reciprocal of 0)
            float distance;
            if( i == 0 ) {
                distance = source;
            }
            else if( i == count-1 ) {
                distance = target;
            }
            else {
                //float alpha = ((float)i)/(count-1.0f);
                // rather than linear interpolation, we use log, see https://stackoverflow.com/questions/5215459/android-mediaplayer-setvolume-function
                // this gives more shots are closer focus distances
                int value = i;
                if( real_focus_distance_s > real_focus_distance_e ) {
                    // if source is further than target, we still want the interpolation distances to be the same, but in reversed order
                    value = count-1-i;
                }
                float alpha = (float)(1.0-Math.log(count-value)/Math.log(count));
                if( real_focus_distance_s > real_focus_distance_e ) {
                    alpha = 1.0f-alpha;
                }
                float real_distance = (1.0f-alpha)*real_focus_distance_s + alpha*real_focus_distance_e;
                distance = 1.0f/real_distance;
            }
            focus_distances.add(distance);
        }
        return focus_distances;
    }

    private int imageIndex = 1;
    private void writeImageToFile() {
        Log.e("TAG", "imageIndex " +imageIndex);
//        String filePath = Environment.getExternalStorageDirectory() + "/DCIM/Camera/001.jpg";
        String filePath = "/data/data/com.example.burstcapture/files/"+imageIndex+".jpg";
        imageIndex++;
        Image image = mImageReader.acquireNextImage();
        if (image == null) {
            return;
        }
        ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
        byte[] data = new byte[byteBuffer.remaining()];
        byteBuffer.get(data);
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File(filePath));
            fos.write(data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
                fos = null;
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                image.close();
                image = null;
            }
        }
    }

    ImageReader.OnImageAvailableListener mReaderImgListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Log.e("TAG", "onImageAvailable: imageIndex " +imageIndex );
            writeImageToFile();
            mImageTaken++;
            if (mImageTaken < 3){
                try {
                    mCaptureSession.capture(mRequests.get(mImageTaken), null, null);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            } else {
                mImageTaken = 0;
            }
        }
    };
}
